/*
 * @Author: 李超越 1509572300@qq.com
 * @Date: 2024-07-29 09:18:59
 * @LastEditors: 李超越 1509572300@qq.com
 * @LastEditTime: 2024-07-30 10:21:03
 * @FilePath: /my_git/lesson24_pipe/mypipe/mytest.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE;
 */

#include "task.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
#include <iostream>
#include <ctime>
using namespace std;

enum
{
    UsageError=1,
    ArgError,
    PipeError
};

class Channel
{
public:
    Channel(int wfd,pid_t sub_id,string& name)
    :_wfd(wfd),_sub_process_id(sub_id),_name(name)
    {}

    void PrintDebug()
    {
        cout<<"_wfd: "<<_wfd;
        cout<<",_sub_process_id: "<<_sub_process_id;
        cout<<",_name: "<<_name;
    }

    string name()
    {
        return _name;
    }

    int wfd()
    {
        return _wfd;
    }
    
    pid_t pid()
    {
        return _sub_process_id;
    }
 
    void Close()
    {
        close(_wfd);
    }

    ~Channel()
    {}

private:
    int _wfd;
    pid_t _sub_process_id;
    string _name;
};

class ProcessPool
{
public:
    ProcessPool(int sub_process_num):_sub_process_num(sub_process_num)
    {}

    //创建子进程
    int CreateProcess(ForkWork* works)
    {
        vector<int> fds;
        for(int num=0;num<_sub_process_num;num++)
        {
            int pipefd[2]={0};
            pid_t id=fork();
            if(id==0)
            {
                if(!fds.empty())
                {
                    for(auto fd:fds)
                    {
                        close(fd);
                    }
                }
                close(pipefd[1]);
                dup2(pipefd[0],0);
                works->work();
                exit(0);
            }
            string cname="channel-"+to_string(num);
            close(pipefd[0]);
            channels.push_back(Channel(pipefd[1],id,cname));
            fds.push_back(pipefd[1]);
        }
        return 0;
    }

    void Debug()
    {
        for(auto cha:channels)
        {
            cha.PrintDebug();
            cout<<endl;
        }
    }

    int NextChannel()
    {
        static int next=0;
        int c=channels[next];
        next%=channels.size();
        return c;
    }

    void SendTaskCode(int channel,uint32_t code)
    {
        cout<<"send code:"<<code<<"to"<<channel[channel].name()<<"sub process id:"<<channels[channel].pid()<<endl;
        write(channels[channel].wfd(),&code,sizeof(code));
    }

    void KillAll()
    {
        for(auto &channel:channels)
        {
            channel.Close();
            cout<<channel.name()<<"close done,sub process quit now"<<channel.pid()<<endl;
        }
    }

    void Wait()
    {
        // for(auto &channel:channels)
        // {
        //     pid_t pid=channel.pid();
        //     pid_t rif=witpid(pid,nullptr,0);
        //     if(rid==pid)
        //     {
        //         cout<<"wait sub process:"<<pid<<endl;
        //     }
        // }
    }

    ~ProcessPool()
    {}

private:
    int _sub_process_num;
    vector<Channel> channels;
};

void Usage(const string &proc)
{
    cout<<"Usage:"<<proc<<"subprocess-num"<<endl;
}

void CtrlProcessPool(ProcessPool *process_ptr,int cnt)
{
    while(true)
    {
        //选择子进程和通道
        int channel=process_ptr->NextChannel();
        //选择任务
        uint32_t code =works.NextTask();
        //发送任务
        process_ptr->SendTaskCode(channel,code);
        sleep(1);
    }

}

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        return 1;
    }
    int sub_process_num=stoi(argv[1]);
    if(sub_process_num<=0)
    {
        return PipeError;
    }
    srand((uint64_t)time(nullptr));
    ForkWork* works;
    
    //创建信道,子进程
    ProcessPool* process_ptr=new ProcessPool(sub_process_num);
    process_ptr->CreateProcess(works);
    process_ptr->Debug();

    //控制子进程
    CtrlProcessPool(process_ptr,sub_process_num);

    //回收子进程
    process_ptr->KillAll();
    process_ptr->Wait();

    delete process_ptr;
    return 0;
}